{
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yvZxs0nFi_LI"
      },
      "source": [
        "[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/04-langchain-chat.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/pinecone-io/examples/blob/master/learn/generation/langchain/handbook/04-langchain-chat.ipynb)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "sdwa2QfAp9pT",
        "outputId": "5a168276-60f7-41f9-9fc2-e1f5d07d6989"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m1.0/1.0 MB\u001b[0m \u001b[31m16.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m2.5/2.5 MB\u001b[0m \u001b[31m38.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m65.3/65.3 kB\u001b[0m \u001b[31m2.0 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m363.0/363.0 kB\u001b[0m \u001b[31m14.1 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m45.2/45.2 kB\u001b[0m \u001b[31m2.3 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[2K   \u001b[90m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\u001b[0m \u001b[32m50.9/50.9 kB\u001b[0m \u001b[31m2.5 MB/s\u001b[0m eta \u001b[36m0:00:00\u001b[0m\n",
            "\u001b[?25h"
          ]
        }
      ],
      "source": [
        "!pip install -qU \\\n",
        "  langchain==0.3.25 \\\n",
        "  langchain-community==0.3.25 \\\n",
        "  langchain-openai==0.3.22 \\\n",
        "  jinja2==3.1.6"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "wnXqS4T_q5m4"
      },
      "source": [
        "We'll start by initializing the `ChatOpenAI` object. For this we'll need an [OpenAI API key](https://platform.openai.com/account/api-keys). Note that there is naturally a small cost to running this notebook due to the paid nature of OpenAI's API access."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "f3xQHFXErQyv",
        "outputId": "b105e220-4260-4261-ab85-6d9b9fdb61d3"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Enter your OpenAI API key: ··········\n"
          ]
        }
      ],
      "source": [
        "import os\n",
        "from getpass import getpass\n",
        "\n",
        "os.environ[\"OPENAI_API_KEY\"] = os.getenv(\"OPENAI_API_KEY\") \\\n",
        "    or getpass(\"Enter your OpenAI API key: \")\n",
        "\n",
        "OPENAI_API_KEY = os.getenv(\"OPENAI_API_KEY\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "_zKBICpJsNqp"
      },
      "source": [
        "Initialize the `ChatOpenAI` object. We'll set `temperature=0` to minimize randomness and make outputs repeatable."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "metadata": {
        "id": "1i7tIsh2rX8Q"
      },
      "outputs": [],
      "source": [
        "from langchain_openai import ChatOpenAI\n",
        "\n",
        "# Initialize the chat model\n",
        "chat = ChatOpenAI(\n",
        "    openai_api_key=OPENAI_API_KEY,\n",
        "    temperature=0,\n",
        "    model='gpt-4.1-mini'\n",
        ")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "RRZFsmH_t3K5"
      },
      "source": [
        "Chats with the Chat-GPT model `gpt-4.1-mini` are typically structured like so:\n",
        "\n",
        "```\n",
        "System: You are a helpful assistant.\n",
        "\n",
        "User: Hi AI, how are you today?\n",
        "\n",
        "Assistant: I'm great thank you. How can I help you?\n",
        "\n",
        "User: I'd like to understand string theory.\n",
        "```\n",
        "\n",
        "The final `\"Assistant:\"` without a response is what would prompt the model to continue the conversation. In the official OpenAI `ChatCompletion` endpoint these would be passed to the model in a format like:\n",
        "\n",
        "```json\n",
        "[\n",
        "    {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n",
        "    {\"role\": \"user\", \"content\": \"Hi AI, how are you today?\"},\n",
        "    {\"role\": \"assistant\", \"content\": \"I'm great thank you. How can I help you?\"},\n",
        "    {\"role\": \"user\", \"content\": \"I'd like to understand string theory.\"}\n",
        "]\n",
        "```\n",
        "\n",
        "In LangChain there is a slightly different format. We use three *message* objects like so:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "jqFLoaRqtl8z",
        "outputId": "a9945991-4be1-4ad1-a97d-a3117049196f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Hello! I'm just a bunch of code, so I don't have feelings, but I'm here and ready to help you. How can I assist you today?\n"
          ]
        }
      ],
      "source": [
        "from langchain.prompts.chat import ChatPromptTemplate\n",
        "from langchain_core.output_parsers import StrOutputParser\n",
        "\n",
        "# Create the message templates\n",
        "system_template = \"You are a helpful assistant.\"\n",
        "human_template = \"{input}\"\n",
        "\n",
        "# Create the prompt template\n",
        "prompt = ChatPromptTemplate.from_messages([\n",
        "    (\"system\", system_template),\n",
        "    (\"human\", human_template)\n",
        "])\n",
        "\n",
        "# Create the chain\n",
        "chain = prompt | chat | StrOutputParser()\n",
        "\n",
        "# Test the chain\n",
        "result = chain.invoke({\"input\": \"Hi AI, how are you today?\"})\n",
        "print(result)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "O7AJh2ACytwc"
      },
      "source": [
        "The format is very similar, we're just swapping the role of `\"user\"` for `HumanMessage`, and the role of `\"assistant\"` for `AIMessage`.\n",
        "\n",
        "We generate the next response from the AI by passing these messages to the `ChatOpenAI` object."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "OKMcBaPR3AAv",
        "outputId": "f7110315-cd4a-4fc9-c25a-a007017312d2"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Certainly! String theory is a theoretical framework in physics that attempts to reconcile quantum mechanics and general relativity, aiming to provide a unified description of all fundamental forces and particles.\n",
            "\n",
            "Here’s a basic overview:\n",
            "\n",
            "1. **Fundamental Idea**: Instead of viewing the smallest building blocks of the universe as point-like particles (like electrons or quarks), string theory proposes that these are tiny, one-dimensional \"strings\" that can vibrate at different frequencies. Each vibration mode corresponds to a different particle.\n",
            "\n",
            "2. **Dimensions**: While we experience the universe in 3 spatial dimensions plus time, string theory requires additional spatial dimensions—typically 10 or 11 total dimensions—to be mathematically consistent. These extra dimensions are thought to be compactified or curled up at scales too small to detect.\n",
            "\n",
            "3. **Types of Strings**: Strings can be open (with two endpoints) or closed (forming loops). Different types of strings and their vibrations give rise to different particles, including force carriers like photons and gravitons.\n",
            "\n",
            "4. **Unification**: One of the main goals of string theory is to unify all fundamental forces—gravity, electromagnetism, the strong nuclear force, and the weak nuclear force—into a single framework.\n",
            "\n",
            "5. **Supersymmetry**: Many versions of string theory incorporate supersymmetry, a proposed symmetry between bosons (force-carrying particles) and fermions (matter particles). This helps solve certain theoretical problems and predicts partner particles for those we know.\n",
            "\n",
            "6. **Current Status**: String theory is still a work in progress. It has not yet been experimentally confirmed, partly because the energy scales involved are far beyond current experimental capabilities. However, it remains a leading candidate for a theory of quantum gravity.\n",
            "\n",
            "If you want, I can explain any of these points in more detail or discuss related topics!\n"
          ]
        }
      ],
      "source": [
        "# Create initial messages\n",
        "prompt = ChatPromptTemplate.from_messages([\n",
        "    (\"system\", \"You are a helpful assistant.\"),\n",
        "    (\"human\", \"Hi AI, how are you today?\"),\n",
        "    (\"ai\", \"I'm great thank you. How can I help you?\"),\n",
        "    (\"human\", \"I'd like to understand string theory.\")\n",
        "])\n",
        "\n",
        "# Create the chain using LCEL pipe syntax\n",
        "chain = prompt | chat | StrOutputParser()\n",
        "\n",
        "# Get response using LCEL\n",
        "res = chain.invoke({})\n",
        "print(res)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "STl7g3PQ3kg8"
      },
      "source": [
        "Because `res` is just another `AIMessage` object, we can append it to `messages`, add another `HumanMessage`, and generate the next response in the conversation."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 7,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 192
        },
        "id": "ESZ6UrBA4PwZ",
        "outputId": "5d6e5a9e-91f0-403b-88fb-2bcad1dacd0e"
      },
      "outputs": [
        {
          "data": {
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            },
            "text/plain": [
              "'Great question! Physicists believe string theory has the potential to produce a \"unified theory\"—often called a \"Theory of Everything\"—because of several key reasons:\\n\\n1. **All Particles as Vibrations of Strings**: In string theory, every fundamental particle is just a different vibrational mode of the same basic object: a string. This means that matter particles (like electrons and quarks) and force-carrying particles (like photons and gravitons) are all manifestations of the same underlying entity. This naturally unifies the description of particles and forces.\\n\\n2. **Inclusion of Gravity**: Unlike the Standard Model of particle physics, which successfully describes three of the four fundamental forces but does not include gravity, string theory inherently includes a particle that behaves like the graviton—the hypothetical quantum particle that mediates gravity. This is a major step toward unifying gravity with quantum mechanics.\\n\\n3. **Mathematical Consistency**: String theory requires a consistent mathematical framework that only works if all forces and particles fit together in a specific way. This constraint means that the theory naturally combines the different forces into a single framework, rather than treating them separately.\\n\\n4. **Supersymmetry and Extra Dimensions**: The incorporation of supersymmetry and extra spatial dimensions allows string theory to resolve many theoretical problems that arise when trying to combine quantum mechanics and gravity. These features help unify the forces by embedding them in a higher-dimensional space where they appear as different aspects of the same fundamental structure.\\n\\n5. **No Free Parameters**: Unlike some other theories, string theory is highly constrained. The properties of particles and forces emerge from the geometry and topology of the extra dimensions and the way strings vibrate, rather than being put in by hand. This suggests a deep underlying unity.\\n\\nIn summary, physicists see string theory as a promising candidate for a unified theory because it provides a single, coherent framework that naturally includes all known particles and forces—including gravity—arising from one fundamental entity: the string. However, it’s important to note that this is still a theoretical proposal and has yet to be experimentally verified.\\n\\nWould you like me to explain any of these points further?'"
            ]
          },
          "execution_count": 7,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "# For the follow-up question, we can extend the existing prompt\n",
        "prompt.extend([\n",
        "    (\"ai\", res),  # Previous AI response\n",
        "    (\"human\", \"Why do physicists believe it can produce a 'unified theory'?\")\n",
        "])\n",
        "\n",
        "# Create the chain using LCEL pipe syntax\n",
        "chain = prompt | chat | StrOutputParser()\n",
        "\n",
        "# Get response\n",
        "result = chain.invoke({})\n",
        "result"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "3jfnGuEK5suC"
      },
      "source": [
        "## New Prompt Templates\n",
        "\n",
        "Alongside what we've seen so far there are also three new prompt templates that we can use. Those are the `SystemMessagePromptTemplate`, `AIMessagePromptTemplate`, and `HumanMessagePromptTemplate`.\n",
        "\n",
        "These are simply an extension of [Langchain's prompt templates](https://www.pinecone.io/learn/series/langchain/langchain-prompt-templates/) that modify the returning \"prompt\" to be a `SystemMessage`, `AIMessage`, or `HumanMessage` object respectively.\n",
        "\n",
        "For now, there are not a huge number of use-cases for these objects. However, they can be useful if:\n",
        "- You want different types of response; AND\n",
        "- The types of response should depend on a set of pre-determined input values; AND\n",
        "- You want to save tokens by not explicitly specifying every possible type of input value in the prompts.\n",
        "\n",
        "This will make more sense with an example. Suppose you want to tailor responses to people from a wide variety of countries. E.g. an LLM powered worldwide translator!\n",
        "\n",
        "Some of the languages listed have been commented out as this is just an illustrative example, but the idea is that we can have many languages and dynamically alter the `HumanMessage` prompt so that we don't have to list all of them every time."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "metadata": {
        "id": "nuZ2f7wci_LK"
      },
      "outputs": [],
      "source": [
        "languages = [\n",
        "    \"English\",\n",
        "    \"Esperanto\",\n",
        "    \"Spanish\",\n",
        "    # \"French\",\n",
        "    # \"German\",\n",
        "    # \"Italian\",\n",
        "    # \"Portuguese\",\n",
        "    # \"Dutch\",\n",
        "    # \"Russian\",\n",
        "    # \"Chinese (Simplified)\",\n",
        "    # \"Chinese (Traditional)\",\n",
        "    # \"Japanese\",\n",
        "    # \"Korean\",\n",
        "    # \"Arabic\",\n",
        "    # \"Hindi\",\n",
        "    # \"Turkish\",\n",
        "    # \"Swedish\",\n",
        "    # \"Danish\",\n",
        "    # \"Norwegian\",\n",
        "    # \"Finnish\",\n",
        "    # \"Polish\",\n",
        "    # \"Czech\",\n",
        "    # \"Hungarian\",\n",
        "    # \"Greek\",\n",
        "    # \"Hebrew\",\n",
        "    # \"Vietnamese\",\n",
        "    # \"Thai\"\n",
        "]"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Sto1isO-i_LK"
      },
      "source": [
        "First let's see what the prompt looks like with single example."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "m62JxYxqi_LK",
        "outputId": "9a865e15-2b5e-4d92-f959-4ab9e6178d20"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "ChatPromptValue(messages=[HumanMessage(content='Translate this input <INPUT_START> I hope when you come the weather will be clement. <INPUT_END>  into Esperanto. Do not include any other text in your response.', additional_kwargs={}, response_metadata={})])"
            ]
          },
          "execution_count": 9,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "from langchain.prompts.chat import HumanMessagePromptTemplate\n",
        "\n",
        "# Create the prompt template\n",
        "human_template = HumanMessagePromptTemplate.from_template(\n",
        "    \"Translate this input <INPUT_START> {input} <INPUT_END>  into {language}. Do not include any other text in your response.\"\n",
        ")\n",
        "chat_prompt = ChatPromptTemplate.from_messages([human_template])\n",
        "\n",
        "# Format with dynamic input\n",
        "chat_prompt_value = chat_prompt.format_prompt(\n",
        "    input=\"I hope when you come the weather will be clement.\", # Extra points if you get the reference.\n",
        "    language=\"Esperanto\"\n",
        ")\n",
        "\n",
        "chat_prompt_value\n"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dMpQ6dOxi_LK"
      },
      "source": [
        "Note that to use `HumanMessagePromptTemplate` as typical a prompt templates with the `.format_prompt` method, we needed to pass it through a `ChatPromptTemplate` object. This is case for all of the new chat-based prompt templates.\n",
        "\n",
        "Using this we return a `ChatPromptValue` object. This can be formatted into a list or string like so:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "-LCL-7wAi_LK",
        "outputId": "013f2e49-ef0c-44d2-dfa7-4b6238328b4f"
      },
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[HumanMessage(content='Translate this input <INPUT_START> I hope when you come the weather will be clement. <INPUT_END>  into Esperanto. Do not include any other text in your response.', additional_kwargs={}, response_metadata={})]"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "chat_prompt_value.to_messages()"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 53
        },
        "id": "7-wYQKNmi_LK",
        "outputId": "7cb380ea-5c9b-42cd-ae1f-aca0a6716555"
      },
      "outputs": [
        {
          "data": {
            "application/vnd.google.colaboratory.intrinsic+json": {
              "type": "string"
            },
            "text/plain": [
              "'Human: Translate this input <INPUT_START> I hope when you come the weather will be clement. <INPUT_END>  into Esperanto. Do not include any other text in your response.'"
            ]
          },
          "execution_count": 11,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "chat_prompt_value.to_string()"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "BsCQlnaYi_LK"
      },
      "source": [
        "Okay, let's see this new approach in action with our list of languages."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YWPa7sGO4w_O",
        "outputId": "216a3d5c-3581-4547-a800-60a8c485f23f"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== Response in English ===\n",
            "I hope when you come the weather will be mild.\n",
            "==================================================\n",
            "\n",
            "=== Response in Esperanto ===\n",
            "Mi esperas, ke kiam vi venos, la vetero estos milda.\n",
            "==================================================\n",
            "\n",
            "=== Response in Spanish ===\n",
            "Espero que cuando vengas el tiempo sea benigno.\n",
            "==================================================\n"
          ]
        }
      ],
      "source": [
        "from langchain.prompts.chat import SystemMessagePromptTemplate\n",
        "\n",
        "# Create the prompt template\n",
        "human_template = HumanMessagePromptTemplate.from_template(\n",
        "    \"Translate this input '{input}' into {language}. Do not include any other text in your response.\"\n",
        ")\n",
        "system_template = SystemMessagePromptTemplate.from_template(\"You are a helpful assistant.\")\n",
        "\n",
        "# Create the chain using LCEL pipe syntax\n",
        "chain = (\n",
        "    ChatPromptTemplate.from_messages([system_template, human_template])\n",
        "    | chat\n",
        "    | StrOutputParser()\n",
        ")\n",
        "\n",
        "# Loop through each language\n",
        "for language in languages:\n",
        "    print(f\"\\n=== Response in {language} ===\")\n",
        "\n",
        "    # Invoke the chain with our inputs\n",
        "    result = chain.invoke({\n",
        "        \"input\": \"I hope when you come the weather will be clement.\",\n",
        "        \"language\": language\n",
        "    })\n",
        "\n",
        "    print(result)\n",
        "    print(\"=\" * 50)  # Separator for readability"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "9riszRQri_LK"
      },
      "source": [
        "Excellent!\n",
        "\n",
        "As you can see, it's successfully translated into different languages based on our inputs, *and we didn't have to use unnecessary tokens by inserting the entire language list into the prompt.*"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "dfLRjgjCFNUY"
      },
      "source": [
        "What if the outputs we need are more complicated? For example, what if the input information is technical information that needs to be formatted in a very specific way for the output?\n",
        "\n",
        "E.g. Say that we want to:\n",
        "1. Input technical information.\n",
        "2. Only translate part of the technical information, not all of the text.\n",
        "3. Maintain the same input structure in the output structure.\n",
        "\n",
        "We can use the prompt templates approach for building an initial system message with a few examples for the chatbot to follow — few-shot training via examples. Let's see what that looks like."
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 13,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "M19Ji8gGCOwk",
        "outputId": "312b7d66-e709-46cd-8271-35f81680c128"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== Technical Translation in English ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Technical Note: This error occurs when the application cannot connect to the database.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Esperanto ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Technical Note: Ĉi tiu eraro okazas kiam la aplikaĵo ne povas konekti al la datumbazo.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Spanish ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Nota técnica: Este error ocurre cuando la aplicación no puede conectarse a la base de datos.\n",
            "================================================================================\n"
          ]
        }
      ],
      "source": [
        "# Create few-shot examples for technical content formatting\n",
        "system_template = SystemMessagePromptTemplate.from_template(\n",
        "    \"\"\"You are a technical translator. You must maintain the exact same format and structure in your translations.\n",
        "    Only translate the explanatory text, keeping all technical terms, numbers, and formatting unchanged.\n",
        "\n",
        "    Example input and output pairs:\n",
        "\n",
        "    Input: \"Error 404: Page not found\"\n",
        "    Output: \"Error 404: Página no encontrada\"\n",
        "\n",
        "    Input: \"Status: 200 OK\n",
        "    Response: {{\n",
        "        'data': 'success',\n",
        "        'message': 'Operation completed'\n",
        "    }}\"\n",
        "    Output: \"Status: 200 OK\n",
        "    Response: {{\n",
        "        'data': 'success',\n",
        "        'message': 'Operación completada'\n",
        "    }}\"\n",
        "    \"\"\"\n",
        ")\n",
        "\n",
        "# Example of a technical input\n",
        "human_template = HumanMessagePromptTemplate.from_template(\n",
        "    \"\"\"Translate this technical information to {language}:\n",
        "\n",
        "    Status: 500 Internal Server Error\n",
        "    Response: {{\n",
        "        'error': 'Database connection failed',\n",
        "        'code': 'DB_001',\n",
        "        'timestamp': '2024-03-20T10:30:00Z'\n",
        "    }}\n",
        "\n",
        "    Technical Note: This error occurs when the application cannot connect to the database.\n",
        "    \"\"\"\n",
        ")\n",
        "\n",
        "# Create the chain using LCEL pipe syntax\n",
        "chain = (\n",
        "    ChatPromptTemplate.from_messages([system_template, human_template])\n",
        "    | chat\n",
        "    | StrOutputParser()\n",
        ")\n",
        "\n",
        "# Loop through each language\n",
        "for language in languages:\n",
        "    print(f\"\\n=== Technical Translation in {language} ===\")\n",
        "\n",
        "    # Invoke the chain with our input\n",
        "    result = chain.invoke({\"language\": language})\n",
        "\n",
        "    print(result)\n",
        "    print(\"=\" * 80)  # Separator for readability"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "5jZTYOHvKb_B"
      },
      "source": [
        "Perfect, we seem to get a good response!\n",
        "\n",
        "Now, it's arguable as to whether all of the above is better than simple f-strings like:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 14,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "gIZz2eewP3k4",
        "outputId": "898cadb4-7310-49f6-d5aa-3b16a56afe25"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== Technical Translation in English ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Technical Note: This error occurs when the application cannot connect to the database.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Esperanto ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Technical Note: Ĉi tiu eraro okazas kiam la aplikaĵo ne povas konekti al la datumbazo.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Spanish ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}\n",
            "\n",
            "Nota técnica: Este error ocurre cuando la aplicación no puede conectarse a la base de datos.\n",
            "================================================================================\n"
          ]
        }
      ],
      "source": [
        "from langchain.schema import SystemMessage, HumanMessage\n",
        "\n",
        "# Create the system message with examples\n",
        "system_message = SystemMessage(content=\"\"\"You are a technical translator. You must maintain the exact same format and structure in your translations.\n",
        "Only translate the explanatory text, keeping all technical terms, numbers, and formatting unchanged.\n",
        "\n",
        "Example input and output pairs:\n",
        "\n",
        "Input: \"Error 404: Page not found\"\n",
        "Output: \"Error 404: Página no encontrada\"\n",
        "\n",
        "Input: \"Status: 200 OK\n",
        "Response: {\n",
        "    'data': 'success',\n",
        "    'message': 'Operation completed'\n",
        "}\"\n",
        "Output: \"Status: 200 OK\n",
        "Response: {\n",
        "    'data': 'success',\n",
        "    'message': 'Operación completada'\n",
        "}\"\n",
        "\"\"\")\n",
        "\n",
        "# Loop through each language\n",
        "for language in languages:\n",
        "    print(f\"\\n=== Technical Translation in {language} ===\")\n",
        "\n",
        "    # Create the human message using f-string\n",
        "    human_message = HumanMessage(content=f\"\"\"Translate this technical information to {language}:\n",
        "\n",
        "    Status: 500 Internal Server Error\n",
        "    Response: {{\n",
        "        'error': 'Database connection failed',\n",
        "        'code': 'DB_001',\n",
        "        'timestamp': '2024-03-20T10:30:00Z'\n",
        "    }}\n",
        "\n",
        "    Technical Note: This error occurs when the application cannot connect to the database.\n",
        "    \"\"\")\n",
        "\n",
        "    # Create messages list\n",
        "    messages = [system_message, human_message]\n",
        "\n",
        "    # Get response\n",
        "    res = chat.invoke(messages)\n",
        "\n",
        "    print(res.content)\n",
        "    print(\"=\" * 80)  # Separator for readability"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yiDVIIkhQwWD"
      },
      "source": [
        "In this example, the above is far simpler. So we wouldn't necessarily recommend using prompt templates over f-strings in all scenarios.\n",
        "\n",
        "One example where Prompt Templates might prove useful is in interpreting specific template format types. For example, suppose a project uses lots of `jinja` templates. Rather than writing our functions that handle the input values, f-strings and which renders the jinja template, LangChain Prompt Templates do all of this for us:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 15,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "qVWvyfhNQivT",
        "outputId": "25969dda-60b6-419e-92f1-2a3f5eca7e3c"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== Technical Translation in English ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {{\n",
            "    'error': 'Database connection failed',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}}\n",
            "\n",
            "Technical Note: This error occurs when the application cannot connect to the database.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Esperanto ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {{\n",
            "    'error': 'Database connection malsukcesis',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}}\n",
            "\n",
            "Teknika Noto: Ĉi tiu eraro okazas kiam la aplikaĵo ne povas konekti al la datumbazo.\n",
            "================================================================================\n",
            "\n",
            "=== Technical Translation in Spanish ===\n",
            "Status: 500 Internal Server Error\n",
            "Response: {{\n",
            "    'error': 'Fallo en la conexión a la Database',\n",
            "    'code': 'DB_001',\n",
            "    'timestamp': '2024-03-20T10:30:00Z'\n",
            "}}\n",
            "\n",
            "Nota Técnica: Este error ocurre cuando la aplicación no puede conectarse a la Database.\n",
            "================================================================================\n"
          ]
        }
      ],
      "source": [
        "# Create few-shot examples for technical content formatting\n",
        "system_template = SystemMessagePromptTemplate.from_template(\n",
        "    \"\"\"You are a technical translator. You must maintain the exact same format and structure in your translations.\n",
        "    Only translate the explanatory text, keeping all technical terms, numbers, and formatting unchanged.\n",
        "\n",
        "    Example input and output pairs:\n",
        "\n",
        "    Input: \"Error 404: Page not found\"\n",
        "    Output: \"Error 404: Página no encontrada\"\n",
        "\n",
        "    Input: \"Status: 200 OK\n",
        "    Response: {% raw %}{{\n",
        "        'data': 'success',\n",
        "        'message': 'Operation completed'\n",
        "    }}{% endraw %}\"\n",
        "    Output: \"Status: 200 OK\n",
        "    Response: {% raw %}{{\n",
        "        'data': 'success',\n",
        "        'message': 'Operación completada'\n",
        "    }}{% endraw %}\"\n",
        "    \"\"\",\n",
        "    template_format=\"jinja2\"\n",
        ")\n",
        "\n",
        "# Example of a technical input using Jinja2's control structures and filters\n",
        "human_template = HumanMessagePromptTemplate.from_template(\n",
        "    \"\"\"Translate this technical information to {{ language|upper }}:\n",
        "\n",
        "    Status: 500 Internal Server Error\n",
        "    Response: {% raw %}{{\n",
        "        'error': 'Database connection failed',\n",
        "        'code': 'DB_001',\n",
        "        'timestamp': '2024-03-20T10:30:00Z'\n",
        "    }}{% endraw %}\n",
        "\n",
        "    Technical Note: This error occurs when the application cannot connect to the database.\n",
        "\n",
        "    {% if language == 'spanish' %}\n",
        "    Note: Please use formal Spanish for technical documentation.\n",
        "    {% elif language == 'french' %}\n",
        "    Note: Please use formal French for technical documentation.\n",
        "    {% else %}\n",
        "    Note: Please maintain a formal tone in the translation.\n",
        "    {% endif %}\n",
        "\n",
        "    {% for term in technical_terms %}\n",
        "    Keep the term \"{{ term }}\" unchanged in the translation.\n",
        "    {% endfor %}\n",
        "    \"\"\",\n",
        "    template_format=\"jinja2\"\n",
        ")\n",
        "\n",
        "# Create the chain using LCEL pipe syntax\n",
        "chain = (\n",
        "    ChatPromptTemplate.from_messages([system_template, human_template])\n",
        "    | chat\n",
        "    | StrOutputParser()\n",
        ")\n",
        "\n",
        "# Loop through each language\n",
        "for language in languages:\n",
        "    print(f\"\\n=== Technical Translation in {language} ===\")\n",
        "\n",
        "    # Invoke the chain with our inputs\n",
        "    result = chain.invoke({\n",
        "        \"language\": language,\n",
        "        \"technical_terms\": ['DB_001', 'Internal Server Error', 'Database connection']\n",
        "    })\n",
        "\n",
        "    print(result)\n",
        "    print(\"=\" * 80)  # Separator for readability"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "kYNYCY0Fi_LL"
      },
      "source": [
        "Let's see what the prompts look like after LangChain interprets the Jinja2 templates. This demonstrates how LangChain automatically handles the template interpretation for us:"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 16,
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "K2P_BmPVi_LL",
        "outputId": "def1bc71-40ad-4c82-9c10-92172680b1e6"
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "\n",
            "=== Formatted Prompt for Spanish ===\n",
            "\n",
            "SYSTEM MESSAGE:\n",
            "----------------------------------------\n",
            "You are a technical translator. You must maintain the exact same format and structure in your translations.\n",
            "    Only translate the explanatory text, keeping all technical terms, numbers, and formatting unchanged.\n",
            "    \n",
            "    Example input and output pairs:\n",
            "    \n",
            "    Input: \"Error 404: Page not found\"\n",
            "    Output: \"Error 404: Página no encontrada\"\n",
            "    \n",
            "    Input: \"Status: 200 OK\n",
            "    Response: {{\n",
            "        'data': 'success',\n",
            "        'message': 'Operation completed'\n",
            "    }}\"\n",
            "    Output: \"Status: 200 OK\n",
            "    Response: {{\n",
            "        'data': 'success',\n",
            "        'message': 'Operación completada'\n",
            "    }}\"\n",
            "    \n",
            "================================================================================\n",
            "\n",
            "HUMAN MESSAGE:\n",
            "----------------------------------------\n",
            "Translate this technical information to SPANISH:\n",
            "    \n",
            "    Status: 500 Internal Server Error\n",
            "    Response: {{\n",
            "        'error': 'Database connection failed',\n",
            "        'code': 'DB_001',\n",
            "        'timestamp': '2024-03-20T10:30:00Z'\n",
            "    }}\n",
            "    \n",
            "    Technical Note: This error occurs when the application cannot connect to the database.\n",
            "    \n",
            "    \n",
            "    Note: Please use formal Spanish for technical documentation.\n",
            "    \n",
            "    \n",
            "    \n",
            "    Keep the term \"DB_001\" unchanged in the translation.\n",
            "    \n",
            "    Keep the term \"Internal Server Error\" unchanged in the translation.\n",
            "    \n",
            "    Keep the term \"Database connection\" unchanged in the translation.\n",
            "    \n",
            "    \n",
            "================================================================================\n"
          ]
        }
      ],
      "source": [
        "# Get the formatted prompt for Spanish\n",
        "print(\"\\n=== Formatted Prompt for Spanish ===\")\n",
        "\n",
        "# Format the prompts with our inputs\n",
        "formatted_prompt = ChatPromptTemplate.from_messages([\n",
        "    system_template,\n",
        "    human_template\n",
        "]).format_prompt(\n",
        "    language='spanish',\n",
        "    technical_terms=['DB_001', 'Internal Server Error', 'Database connection']\n",
        ")\n",
        "\n",
        "# Print the formatted messages\n",
        "for message in formatted_prompt.to_messages():\n",
        "    print(f\"\\n{message.type.upper()} MESSAGE:\")\n",
        "    print(\"-\" * 40)\n",
        "    print(message.content)\n",
        "    print(\"=\" * 80)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "Bb4DmDPxi_LL"
      },
      "source": [
        "Let's break down how LangChain automatically interpreted the Jinja2 templates in our prompts:\n",
        "\n",
        "1. **Language Filter and Variable**:\n",
        "   - Original: `{{ language|upper }}`\n",
        "   - Interpreted as: `SPANISH`\n",
        "   - The `|upper` filter automatically converted the language to uppercase\n",
        "\n",
        "2. **Conditional Logic**:\n",
        "   - Original:\n",
        "     ```jinja2\n",
        "     {% if language == 'spanish' %}\n",
        "     Note: Please use formal Spanish for technical documentation.\n",
        "     {% elif language == 'french' %}\n",
        "     Note: Please use formal French for technical documentation.\n",
        "     {% else %}\n",
        "     Note: Please maintain a formal tone in the translation.\n",
        "     {% endif %}\n",
        "     ```\n",
        "   - Interpreted as: `Note: Please use formal Spanish for technical documentation.`\n",
        "   - The `if` statement automatically selected the Spanish-specific note\n",
        "\n",
        "3. **Loop Structure**:\n",
        "   - Original:\n",
        "     ```jinja2\n",
        "     {% for term in technical_terms %}\n",
        "     Keep the term \"{{ term }}\" unchanged in the translation.\n",
        "     {% endfor %}\n",
        "     ```\n",
        "   - Interpreted as three separate lines, one for each technical term:\n",
        "     ```\n",
        "     Keep the term \"DB_001\" unchanged in the translation.\n",
        "     Keep the term \"Internal Server Error\" unchanged in the translation.\n",
        "     Keep the term \"Database connection\" unchanged in the translation.\n",
        "     ```\n",
        "   - The `for` loop automatically iterated through our list of technical terms\n",
        "\n",
        "4. **Raw JSON Blocks**:\n",
        "   - Original: `{% raw %}{{ ... }}{% endraw %}`\n",
        "   - Interpreted as: `{{ ... }}`\n",
        "   - The `raw` tags were automatically removed while preserving the JSON structure"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "examples",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.12.7"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}
